/*
 * Copyright 2007 Agile Plugins Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.agileplugins.monkey;

import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiAssignmentExpression;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiDeclarationStatement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiExpressionList;
import com.intellij.psi.PsiExpressionStatement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.psi.PsiLocalVariable;
import com.intellij.psi.PsiNewExpression;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiReturnStatement;
import com.intellij.psi.PsiStatement;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.util.IncorrectOperationException;

public class InstanceProviderIntention implements IntentionAction {
    boolean createReturnType;
    String returnTypeName;
    private Logger logger = Logger.getInstance(getClass().getName());
    private PsiStatement statement;
    private PsiNewExpression newExpression;

    public String getText() {
        return "Convert to an InstanceProvider call";
    }

    public String getFamilyName() {
        return "PurpleMonkeyDishwasher";
    }

    public boolean isAvailable(Project project, Editor editor, PsiFile psiFile) {
        int offset = editor.getCaretModel().getOffset();
        PsiElement element = psiFile.findElementAt(offset);
        if (element instanceof PsiWhiteSpace) {
            element = element.getPrevSibling();
        }
        statement = getStatement(element);
        if (statement == null) {
            return false;
        }
        PsiExpression mainExpression = null;
        if (statement instanceof PsiExpressionStatement) {
            PsiExpressionStatement exp = (PsiExpressionStatement) statement;
            mainExpression = exp.getExpression();
            if (mainExpression instanceof PsiAssignmentExpression) {
                PsiAssignmentExpression ass = (PsiAssignmentExpression) mainExpression;
                PsiExpression lExpression = ass.getLExpression();
                returnTypeName = lExpression.getText();
                createReturnType = false;
                mainExpression = ass.getRExpression();
            } else {
                createReturnType = true;
                returnTypeName = null;
            }
        } else if (statement instanceof PsiDeclarationStatement) {
            PsiDeclarationStatement dec = (PsiDeclarationStatement) statement;
            PsiElement[] declaredElements = dec.getDeclaredElements();
            if (declaredElements.length == 1) {
                PsiElement declaredElement = declaredElements[0];
                if (declaredElement instanceof PsiLocalVariable) {
                    PsiLocalVariable loc = (PsiLocalVariable) declaredElement;
                    mainExpression = loc.getInitializer();
                    returnTypeName = loc.getName();
                    createReturnType = true;
                }
            }
        } else if (statement instanceof PsiReturnStatement) {
            PsiReturnStatement psiReturnStatement = (PsiReturnStatement) statement;
            mainExpression = psiReturnStatement.getReturnValue();
            returnTypeName = null;
            createReturnType = false;
        }
        if (mainExpression instanceof PsiNewExpression) {
            newExpression = (PsiNewExpression) mainExpression;
            return true;
        }
        return false;
    }

    public PsiStatement getStatement(PsiElement element) {
        while (element != null && !(element instanceof PsiStatement)) {
            element = element.getParent();
        }
        return (PsiStatement) element;
    }

    public void invoke(Project project, Editor editor, PsiFile psiFile) throws IncorrectOperationException {
        PsiJavaCodeReferenceElement classReference = newExpression.getClassReference();
        if (classReference == null) {
            return;
        }
        PsiElement psiElement = classReference.resolve();
        if (!(psiElement instanceof PsiClass)) {
            return;
        }
        PsiExpressionList argumentList = newExpression.getArgumentList();
        PsiExpression[] expressions = argumentList.getExpressions();
        String className = classReference.getText();
        StringBuffer sb = new StringBuffer();
        sb.append("(");
        sb.append(className).append(")getInstanceProvider().newInstance(");
        sb.append(className + ".class");
        if (expressions.length > 0) {
            sb.append(", ");
            if (expressions.length > 1) {
                sb.append("new Object[]{");
            }
            for (int i = 0; i < expressions.length; i++) {
                PsiExpression expression = expressions[i];
                if (i > 0) {
                    sb.append(", ");
                }
                PsiType type = expression.getType();
                if (type instanceof PsiPrimitiveType) {
                    PsiPrimitiveType primitiveType = (PsiPrimitiveType) type;
                    sb.append(getWrapper(primitiveType.getPresentableText(), expression.getText()));
                } else {
                    sb.append(expression.getText());
                }
            }
            if (expressions.length > 1) {
                sb.append("}");
            }
        }
        sb.append(")");
        PsiElementFactory elementFactory = statement.getManager().getElementFactory();
        PsiExpression expressionFromText = elementFactory.createExpressionFromText(sb.toString(), statement);
        newExpression.replace(expressionFromText);
    }

    public boolean startInWriteAction() {
        return true;
    }

    private String getWrapper(String primitiveType, String expression) {
        String wrappedType = getWrappedType(primitiveType);
        if ("boolean".equals(primitiveType)) {
            if ("true".equals(expression)) {
                return "Boolean.TRUE";
            }
            if ("false".equals(expression)) {
                return "Boolean.FALSE";
            }
            return "Boolean.valueOf(" + expression + ")";
        }
        return "new " + wrappedType + "(" + expression + ")";
    }

    private String getWrappedType(String primitiveType) {
        if ("int".equals(primitiveType)) {
            return "Integer";
        }
        return primitiveType.substring(0, 1).toUpperCase() + primitiveType.substring(1);
    }
}
